#pragma once
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <glm/glm.hpp>
#include <Geometry/ele_type.h>

namespace PhysLeo {

/**
 * Smesh class, only store the geometry info, can represent Hex Tet Quad Tet uniform mesh now.
 */
class Smesh {
public:

    /**
    * default constructor.
    * set ele_type_ to NONE, set vertex_num_ ele_num_ to 0
    */
	Smesh():vertex_num_(0),ele_num_(0),ele_type_(EleType::NONE){}

    /**
    * return the number of vertices
    * @return a int value, the number of vertices
    */
    int vertexNum() const {return vertex_num_;}

    /**
    * return the number of elements
    * @return a int value, the number of elements
    */
    int eleNum() const {return ele_num_;}

    /**
    * return the element type
    * @return a EleType enum, element type
    */
    EleType eleType() const {return ele_type_;}

    /**
    * return the number of vertices in an element
    * @return a int value, the number of vertices in an element
    */
    int eleVertNum() const
    {
        return eleVertNumFromEleType(ele_type_);
    }

    /**
    * return vertices data
    * @return vector<float>, the vertices data
    */
    std::vector<glm::tvec3<float>>& vertices() {return pos_;}

    /**
    * return elements data
    * @return vector<int>, the elements data
    */
    std::vector<int>& elements() {return ele_;}

    /**
    * return elements volume data
    * @return vector<float>, the elements volume data
    */
    std::vector<float>& eleVolume() { return ele_volume_; }

    /**
    * return vertices volume data
    * @return vector<float>, the vertices volume data
    */
    std::vector<float>& vertVolume() { return vert_volume_; }

    /**
    * load mesh from file
    * now support files: smesh, obj(surface mesh), abq(large model factory), inp(hypermesh)
    * @param[in] filename  std::string type, better use absolute path
    */
    void loadFromFile(const std::string &filename)
    {
        pos_.clear();
        ele_.clear();
        ele_type_=EleType::NONE;

        std::fstream file(filename.c_str());
	    if (!file.is_open()) 
        { 
            std::cout << "open file error!" << filename << std::endl;
            return; 
        }
	    std::cout << "reading file: " << filename << std::endl;

        const auto extension = filename.substr(filename.find_last_of('.'));
        if(extension==".obj")loadFromObj(filename);
        else if(extension==".abq")loadFromAbq(file);
        else if(extension==".inp")loadFromInp(file);
        else if(extension==".smesh")loadFromSmesh(file);
        else std::cout<<"can't identify this format"<<std::endl;

        vertex_num_ = static_cast<int>(pos_.size());
        ele_num_ = static_cast<int>(ele_.size())/eleVertNum();
        file.close();

        computeVolume();
    }

private:
    /**
    * load mesh from obj file
    * @param[in] filename  std::string type, better use absolute path
    */
    void loadFromObj(const std::string& filename);

    /**
    * load mesh from inp file
    * @param[in] file  std::fstream type
    */
    void loadFromInp(std::fstream &file);

    /**
    * load mesh from abq file
    * @param[in] file  std::fstream type
    */
    void loadFromAbq(std::fstream &file);

    /**
    * load mesh from smesh file
    * @param[in] file  std::fstream type
    */
    void loadFromSmesh(std::fstream &file);

    /**
     * compute element and vertex volume
     */
    void computeVolume();

    /**
    * number of vertices 
    */
    int vertex_num_;

    /**
    * number of elements
    */
    int ele_num_;

    /**
    * element type
    */
    EleType ele_type_;

    /**
    * vertices data
    */
	std::vector<glm::tvec3<float>> pos_;

    /**
    * elements data
    */
	std::vector<int> ele_;

    /**
     * elements volume area or length for 3D 2D 1D situation
     */
    std::vector<float> ele_volume_;

    /**
     * vertex volume area or length for 3D 2D 1D situation
     */
    std::vector<float> vert_volume_;
};

}